1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use crate::utils::files::*;
7use anyhow::Result;
8use serde::{Deserialize, Serialize};
9use std::io::{Read, Seek, Write};
10
11#[derive(Debug)]
12pub struct Abmp10ImageBuilder {}
14
15impl Abmp10ImageBuilder {
16 pub fn new() -> Self {
17 Self {}
18 }
19}
20
21impl ScriptBuilder for Abmp10ImageBuilder {
22 fn default_encoding(&self) -> Encoding {
23 Encoding::Cp932
24 }
25
26 fn build_script(
27 &self,
28 buf: Vec<u8>,
29 _filename: &str,
30 encoding: Encoding,
31 _archive_encoding: Encoding,
32 config: &ExtraConfig,
33 _archive: Option<&Box<dyn Script>>,
34 ) -> Result<Box<dyn Script>> {
35 Ok(Box::new(Abmp10Image::new(
36 MemReader::new(buf),
37 encoding,
38 config,
39 )?))
40 }
41
42 fn extensions(&self) -> &'static [&'static str] {
43 &["b"]
44 }
45
46 fn script_type(&self) -> &'static ScriptType {
47 &ScriptType::QlieAbmp10
48 }
49
50 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
51 if buf_len >= 6 && buf.starts_with(b"abmp1") {
52 let v = buf[5];
53 if v >= b'0' && v <= b'2' {
54 return Some(25);
55 }
56 }
57 None
58 }
59
60 fn can_create_file(&self) -> bool {
61 true
62 }
63
64 fn create_file<'a>(
65 &'a self,
66 filename: &'a str,
67 writer: Box<dyn WriteSeek + 'a>,
68 encoding: Encoding,
69 file_encoding: Encoding,
70 config: &ExtraConfig,
71 ) -> Result<()> {
72 create_file(filename, writer, encoding, file_encoding, config)
73 }
74}
75
76trait AbmpRes {
77 fn read_from<T: Read + Seek>(
78 data: &mut T,
79 encoding: Encoding,
80 img: &mut AbmpImage,
81 ) -> Result<Self>
82 where
83 Self: Sized;
84 fn write_to<T: Write + Seek>(
85 &self,
86 data: &mut T,
87 encoding: Encoding,
88 img: &AbmpImage,
89 ) -> Result<()>;
90}
91
92#[derive(Clone, Debug, Serialize, Deserialize)]
93struct ResourceRef {
94 index: usize,
95}
96
97#[derive(Clone, Debug, Serialize, Deserialize)]
98struct AbData {
99 tag: String,
101 data: ResourceRef,
102}
103
104impl AbmpRes for AbData {
105 fn read_from<T: Read + Seek>(
106 data: &mut T,
107 encoding: Encoding,
108 img: &mut AbmpImage,
109 ) -> Result<Self>
110 where
111 Self: Sized,
112 {
113 let tag = data.read_fstring(0x10, encoding, true)?;
114 if !tag.starts_with("abdata") {
115 anyhow::bail!("Invalid AbData tag: {}", tag);
116 }
117 let size = data.read_u32()?;
118 let resource = data.read_exact_vec(size as usize)?;
119 img.resources.push(resource);
120 let index = img.resources.len() - 1;
121 img.resource_filenames.push(format!("{tag}_{index}"));
122 Ok(AbData {
123 tag,
124 data: ResourceRef { index },
125 })
126 }
127
128 fn write_to<T: Write + Seek>(
129 &self,
130 data: &mut T,
131 encoding: Encoding,
132 img: &AbmpImage,
133 ) -> Result<()> {
134 data.write_fstring(&self.tag, 0x10, encoding, 0, false)?;
135 let res = img
136 .resources
137 .get(self.data.index)
138 .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?;
139 data.write_u32(res.len() as u32)?;
140 data.write_all(res)?;
141 Ok(())
142 }
143}
144
145#[derive(Clone, Debug, Serialize, Deserialize)]
146struct AbImage10 {
148 datas: Vec<AbmpResource>,
149}
150
151impl AbmpRes for AbImage10 {
152 fn read_from<T: Read + Seek>(
153 data: &mut T,
154 encoding: Encoding,
155 img: &mut AbmpImage,
156 ) -> Result<Self>
157 where
158 Self: Sized,
159 {
160 let tag = data.read_fstring(0x10, encoding, true)?;
161 if tag != "abimage10" {
162 anyhow::bail!("Invalid AbImage10 tag: {}", tag);
163 }
164 let mut datas = Vec::new();
165 let count = data.read_u8()?;
166 for _ in 0..count {
167 let data = AbmpResource::read_from(data, encoding, img)?;
168 datas.push(data);
169 }
170 Ok(AbImage10 { datas })
171 }
172
173 fn write_to<T: Write + Seek>(
174 &self,
175 data: &mut T,
176 encoding: Encoding,
177 img: &AbmpImage,
178 ) -> Result<()> {
179 data.write_fstring("abimage10", 0x10, encoding, 0, false)?;
180 data.write_u8(self.datas.len() as u8)?;
181 for res in &self.datas {
182 res.write_to(data, encoding, img)?;
183 }
184 Ok(())
185 }
186}
187
188#[derive(Clone, Debug, Serialize, Deserialize)]
189struct AbSound10 {
191 datas: Vec<AbmpResource>,
192}
193
194impl AbmpRes for AbSound10 {
195 fn read_from<T: Read + Seek>(
196 data: &mut T,
197 encoding: Encoding,
198 img: &mut AbmpImage,
199 ) -> Result<Self>
200 where
201 Self: Sized,
202 {
203 let tag = data.read_fstring(0x10, encoding, true)?;
204 if tag != "absound10" {
205 anyhow::bail!("Invalid AbSound10 tag: {}", tag);
206 }
207 let mut datas = Vec::new();
208 let count = data.read_u8()?;
209 for _ in 0..count {
210 let data = AbmpResource::read_from(data, encoding, img)?;
211 datas.push(data);
212 }
213 Ok(AbSound10 { datas })
214 }
215
216 fn write_to<T: Write + Seek>(
217 &self,
218 data: &mut T,
219 encoding: Encoding,
220 img: &AbmpImage,
221 ) -> Result<()> {
222 data.write_fstring("absound10", 0x10, encoding, 0, false)?;
223 data.write_u8(self.datas.len() as u8)?;
224 for res in &self.datas {
225 res.write_to(data, encoding, img)?;
226 }
227 Ok(())
228 }
229}
230
231#[derive(Clone, Debug, Serialize, Deserialize)]
232struct AbImgData15 {
234 version: u32,
235 name: String,
236 internal_name: String,
237 typ: u8,
238 param: Vec<u8>,
239 data: ResourceRef,
240}
241
242impl AbmpRes for AbImgData15 {
243 fn read_from<T: Read + Seek>(
244 data: &mut T,
245 encoding: Encoding,
246 img: &mut AbmpImage,
247 ) -> Result<Self>
248 where
249 Self: Sized,
250 {
251 let tag = data.read_fstring(0x10, encoding, true)?;
252 if tag != "abimgdat15" {
253 anyhow::bail!("Invalid AbImgData15 tag: {}", tag);
254 }
255 let version = data.read_u32()?;
256 let name_length = data.read_u16()? as usize * 2;
257 let name = data.read_fstring(name_length, Encoding::Utf16LE, false)?;
258 let internal_name_length = data.read_u16()? as usize;
259 let internal_name = data.read_fstring(internal_name_length, encoding, false)?;
260 let typ = data.read_u8()?;
261 let param_size = if version == 2 { 0x1d } else { 0x11 };
262 let param = data.read_exact_vec(param_size)?;
263 let size = data.read_u32()?;
264 let resource = data.read_exact_vec(size as usize)?;
265 img.resources.push(resource);
266 let index = img.resources.len() - 1;
267 let mut nname = if !name.is_empty() {
268 name.clone()
269 } else if !internal_name.is_empty() {
270 internal_name.clone()
271 } else {
272 format!("abimage15_{index}")
273 };
274 match typ {
275 0 => nname.push_str(".bmp"),
276 1 => nname.push_str(".jpg"),
277 2 | 3 => nname.push_str(".png"),
278 4 => nname.push_str(".m"),
279 5 => nname.push_str(".argb"),
280 6 => nname.push_str(".b"),
281 7 => nname.push_str(".ogv"),
282 8 => nname.push_str(".mdl"),
283 _ => {}
284 }
285 img.resource_filenames.push(nname);
286 Ok(AbImgData15 {
287 version,
288 name,
289 internal_name,
290 typ,
291 param,
292 data: ResourceRef { index },
293 })
294 }
295
296 fn write_to<T: Write + Seek>(
297 &self,
298 data: &mut T,
299 encoding: Encoding,
300 img: &AbmpImage,
301 ) -> Result<()> {
302 data.write_fstring("abimgdat15", 0x10, encoding, 0, false)?;
303 data.write_u32(self.version)?;
304 let name_length = self.name.encode_utf16().count() as u16;
305 let name = encode_string(Encoding::Utf16LE, &self.name, true)?;
306 if name.len() != (name_length as usize) * 2 {
307 anyhow::bail!("Name length mismatch when writing AbImgData15");
308 }
309 data.write_u16(name_length)?;
310 data.write_all(&name)?;
311 let internal_name = encode_string(encoding, &self.internal_name, true)?;
312 data.write_u16(internal_name.len() as u16)?;
313 data.write_all(&internal_name)?;
314 data.write_u8(self.typ)?;
315 let param_size = if self.version == 2 { 0x1d } else { 0x11 };
316 if self.param.len() != param_size {
317 anyhow::bail!("Param size mismatch when writing AbImgData15");
318 }
319 data.write_all(&self.param)?;
320 let res = img
321 .resources
322 .get(self.data.index)
323 .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?;
324 data.write_u32(res.len() as u32)?;
325 data.write_all(res)?;
326 Ok(())
327 }
328}
329
330#[derive(Clone, Debug, Serialize, Deserialize)]
331struct AbImgData14 {
333 name: String,
334 internal_name: String,
335 typ: u8,
336 param: Vec<u8>,
337 data: ResourceRef,
338}
339
340impl AbmpRes for AbImgData14 {
341 fn read_from<T: Read + Seek>(
342 data: &mut T,
343 encoding: Encoding,
344 img: &mut AbmpImage,
345 ) -> Result<Self>
346 where
347 Self: Sized,
348 {
349 let tag = data.read_fstring(0x10, encoding, true)?;
350 if tag != "abimgdat14" {
351 anyhow::bail!("Invalid AbImgData14 tag: {}", tag);
352 }
353 let name_length = data.read_u16()? as usize;
354 let name = data.read_fstring(name_length, encoding, false)?;
355 let internal_name_length = data.read_u16()? as usize;
356 let internal_name = data.read_fstring(internal_name_length, encoding, false)?;
357 let typ = data.read_u8()?;
358 let param = data.read_exact_vec(0x4C)?;
359 let size = data.read_u32()?;
360 let resource = data.read_exact_vec(size as usize)?;
361 img.resources.push(resource);
362 let index = img.resources.len() - 1;
363 let mut nname = if !name.is_empty() {
364 name.clone()
365 } else if !internal_name.is_empty() {
366 internal_name.clone()
367 } else {
368 format!("abimage14_{index}")
369 };
370 match typ {
371 0 => nname.push_str(".bmp"),
372 1 => nname.push_str(".jpg"),
373 2 | 3 => nname.push_str(".png"),
374 4 => nname.push_str(".m"),
375 5 => nname.push_str(".argb"),
376 6 => nname.push_str(".b"),
377 7 => nname.push_str(".ogv"),
378 8 => nname.push_str(".mdl"),
379 _ => {}
380 }
381 img.resource_filenames.push(nname);
382 Ok(AbImgData14 {
383 name,
384 internal_name,
385 typ,
386 param,
387 data: ResourceRef { index },
388 })
389 }
390
391 fn write_to<T: Write + Seek>(
392 &self,
393 data: &mut T,
394 encoding: Encoding,
395 img: &AbmpImage,
396 ) -> Result<()> {
397 data.write_fstring("abimgdat14", 0x10, encoding, 0, false)?;
398 let name = encode_string(encoding, &self.name, true)?;
399 data.write_u16(name.len() as u16)?;
400 data.write_all(&name)?;
401 let internal_name = encode_string(encoding, &self.internal_name, true)?;
402 data.write_u16(internal_name.len() as u16)?;
403 data.write_all(&internal_name)?;
404 data.write_u8(self.typ)?;
405 if self.param.len() != 0x4C {
406 anyhow::bail!("Param size mismatch when writing AbImgData14");
407 }
408 data.write_all(&self.param)?;
409 let res = img
410 .resources
411 .get(self.data.index)
412 .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?;
413 data.write_u32(res.len() as u32)?;
414 data.write_all(res)?;
415 Ok(())
416 }
417}
418
419#[derive(Clone, Debug, Serialize, Deserialize)]
420struct AbImgData13 {
422 name: String,
423 internal_name: String,
424 typ: u8,
425 param: Vec<u8>,
426 data: ResourceRef,
427}
428
429impl AbmpRes for AbImgData13 {
430 fn read_from<T: Read + Seek>(
431 data: &mut T,
432 encoding: Encoding,
433 img: &mut AbmpImage,
434 ) -> Result<Self>
435 where
436 Self: Sized,
437 {
438 let tag = data.read_fstring(0x10, encoding, true)?;
439 if tag != "abimgdat13" {
440 anyhow::bail!("Invalid AbImgData13 tag: {}", tag);
441 }
442 let name_length = data.read_u16()? as usize;
443 let name = data.read_fstring(name_length, encoding, false)?;
444 let internal_name_length = data.read_u16()? as usize;
445 let internal_name = data.read_fstring(internal_name_length, encoding, false)?;
446 let typ = data.read_u8()?;
447 let param = data.read_exact_vec(0xC)?;
448 let size = data.read_u32()?;
449 let resource = data.read_exact_vec(size as usize)?;
450 img.resources.push(resource);
451 let index = img.resources.len() - 1;
452 let mut nname = if !name.is_empty() {
453 name.clone()
454 } else if !internal_name.is_empty() {
455 internal_name.clone()
456 } else {
457 format!("abimage13_{index}")
458 };
459 match typ {
460 0 => nname.push_str(".bmp"),
461 1 => nname.push_str(".jpg"),
462 2 | 3 => nname.push_str(".png"),
463 4 => nname.push_str(".m"),
464 5 => nname.push_str(".argb"),
465 6 => nname.push_str(".b"),
466 7 => nname.push_str(".ogv"),
467 8 => nname.push_str(".mdl"),
468 _ => {}
469 }
470 img.resource_filenames.push(nname);
471 Ok(AbImgData13 {
472 name,
473 internal_name,
474 typ,
475 param,
476 data: ResourceRef { index },
477 })
478 }
479
480 fn write_to<T: Write + Seek>(
481 &self,
482 data: &mut T,
483 encoding: Encoding,
484 img: &AbmpImage,
485 ) -> Result<()> {
486 data.write_fstring("abimgdat13", 0x10, encoding, 0, false)?;
487 let name = encode_string(encoding, &self.name, true)?;
488 data.write_u16(name.len() as u16)?;
489 data.write_all(&name)?;
490 let internal_name = encode_string(encoding, &self.internal_name, true)?;
491 data.write_u16(internal_name.len() as u16)?;
492 data.write_all(&internal_name)?;
493 data.write_u8(self.typ)?;
494 if self.param.len() != 0xC {
495 anyhow::bail!("Param size mismatch when writing AbImgData13");
496 }
497 data.write_all(&self.param)?;
498 let res = img
499 .resources
500 .get(self.data.index)
501 .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?;
502 data.write_u32(res.len() as u32)?;
503 data.write_all(res)?;
504 Ok(())
505 }
506}
507
508#[derive(Clone, Debug, Serialize, Deserialize)]
509struct AbImgData11 {
511 name: String,
512 internal_name: String,
513 typ: u8,
514 data: ResourceRef,
515}
516
517impl AbmpRes for AbImgData11 {
518 fn read_from<T: Read + Seek>(
519 data: &mut T,
520 encoding: Encoding,
521 img: &mut AbmpImage,
522 ) -> Result<Self>
523 where
524 Self: Sized,
525 {
526 let tag = data.read_fstring(0x10, encoding, true)?;
527 if tag != "abimgdat11" {
528 anyhow::bail!("Invalid AbImgData11 tag: {}", tag);
529 }
530 let name_length = data.read_u16()? as usize;
531 let name = data.read_fstring(name_length, encoding, false)?;
532 let internal_name_length = data.read_u16()? as usize;
533 let internal_name = data.read_fstring(internal_name_length, encoding, false)?;
534 let typ = data.read_u8()?;
535 let size = data.read_u32()?;
536 let resource = data.read_exact_vec(size as usize)?;
537 img.resources.push(resource);
538 let index = img.resources.len() - 1;
539 let mut nname = if !name.is_empty() {
540 name.clone()
541 } else if !internal_name.is_empty() {
542 internal_name.clone()
543 } else {
544 format!("abimage11_{index}")
545 };
546 match typ {
547 0 => nname.push_str(".bmp"),
548 1 => nname.push_str(".jpg"),
549 2 | 3 => nname.push_str(".png"),
550 4 => nname.push_str(".m"),
551 5 => nname.push_str(".argb"),
552 6 => nname.push_str(".b"),
553 7 => nname.push_str(".ogv"),
554 8 => nname.push_str(".mdl"),
555 _ => {}
556 }
557 img.resource_filenames.push(nname);
558 Ok(AbImgData11 {
559 name,
560 internal_name,
561 typ,
562 data: ResourceRef { index },
563 })
564 }
565
566 fn write_to<T: Write + Seek>(
567 &self,
568 data: &mut T,
569 encoding: Encoding,
570 img: &AbmpImage,
571 ) -> Result<()> {
572 data.write_fstring("abimgdat11", 0x10, encoding, 0, false)?;
573 let name = encode_string(encoding, &self.name, true)?;
574 data.write_u16(name.len() as u16)?;
575 data.write_all(&name)?;
576 let internal_name = encode_string(encoding, &self.internal_name, true)?;
577 data.write_u16(internal_name.len() as u16)?;
578 data.write_all(&internal_name)?;
579 data.write_u8(self.typ)?;
580 let res = img
581 .resources
582 .get(self.data.index)
583 .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?;
584 data.write_u32(res.len() as u32)?;
585 data.write_all(res)?;
586 Ok(())
587 }
588}
589
590#[derive(Clone, Debug, Serialize, Deserialize)]
591struct AbSndData12 {
593 version: u32,
594 name: String,
595 internal_name: String,
596 typ: u8,
597 data: ResourceRef,
598}
599
600impl AbmpRes for AbSndData12 {
601 fn read_from<T: Read + Seek>(
602 data: &mut T,
603 encoding: Encoding,
604 img: &mut AbmpImage,
605 ) -> Result<Self>
606 where
607 Self: Sized,
608 {
609 let tag = data.read_fstring(0x10, encoding, true)?;
610 if tag != "absnddat12" {
611 anyhow::bail!("Invalid AbSndData12 tag: {}", tag);
612 }
613 let version = data.read_u32()?;
614 let name_length = data.read_u16()? as usize * 2;
615 let name = data.read_fstring(name_length, Encoding::Utf16LE, false)?;
616 let internal_name_length = data.read_u16()? as usize;
617 let internal_name = data.read_fstring(internal_name_length, encoding, false)?;
618 let typ = data.read_u8()?;
619 let size = data.read_u32()?;
620 let resource = data.read_exact_vec(size as usize)?;
621 img.resources.push(resource);
622 let index = img.resources.len() - 1;
623 let mut nname = if !name.is_empty() {
624 name.clone()
625 } else if !internal_name.is_empty() {
626 internal_name.clone()
627 } else {
628 format!("absnddat12_{index}")
629 };
630 match typ {
631 1 => nname.push_str(".ogg"),
632 _ => {}
633 }
634 img.resource_filenames.push(nname);
635 Ok(AbSndData12 {
636 version,
637 name,
638 internal_name,
639 typ,
640 data: ResourceRef { index },
641 })
642 }
643
644 fn write_to<T: Write + Seek>(
645 &self,
646 data: &mut T,
647 encoding: Encoding,
648 img: &AbmpImage,
649 ) -> Result<()> {
650 data.write_fstring("absnddat12", 0x10, encoding, 0, false)?;
651 data.write_u32(self.version)?;
652 let name_length = self.name.encode_utf16().count() as u16;
653 let name = encode_string(Encoding::Utf16LE, &self.name, true)?;
654 if name.len() != (name_length as usize) * 2 {
655 anyhow::bail!("Name length mismatch when writing AbSndData12");
656 }
657 data.write_u16(name_length)?;
658 data.write_all(&name)?;
659 let internal_name = encode_string(encoding, &self.internal_name, true)?;
660 data.write_u16(internal_name.len() as u16)?;
661 data.write_all(&internal_name)?;
662 data.write_u8(self.typ)?;
663 let res = img
664 .resources
665 .get(self.data.index)
666 .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?;
667 data.write_u32(res.len() as u32)?;
668 data.write_all(res)?;
669 Ok(())
670 }
671}
672
673#[derive(Clone, Debug, Serialize, Deserialize)]
674struct AbSndData11 {
676 name: String,
677 internal_name: String,
678 typ: u8,
679 data: ResourceRef,
680}
681
682impl AbmpRes for AbSndData11 {
683 fn read_from<T: Read + Seek>(
684 data: &mut T,
685 encoding: Encoding,
686 img: &mut AbmpImage,
687 ) -> Result<Self>
688 where
689 Self: Sized,
690 {
691 let tag = data.read_fstring(0x10, encoding, true)?;
692 if tag != "absnddat11" {
693 anyhow::bail!("Invalid AbSndData11 tag: {}", tag);
694 }
695 let name_length = data.read_u16()? as usize;
696 let name = data.read_fstring(name_length, encoding, false)?;
697 let internal_name_length = data.read_u16()? as usize;
698 let internal_name = data.read_fstring(internal_name_length, encoding, false)?;
699 let typ = data.read_u8()?;
700 let size = data.read_u32()?;
701 let resource = data.read_exact_vec(size as usize)?;
702 img.resources.push(resource);
703 let index = img.resources.len() - 1;
704 let mut nname = if !name.is_empty() {
705 name.clone()
706 } else if !internal_name.is_empty() {
707 internal_name.clone()
708 } else {
709 format!("absnddat11_{index}")
710 };
711 match typ {
712 1 => nname.push_str(".ogg"),
713 _ => {}
714 }
715 img.resource_filenames.push(nname);
716 Ok(AbSndData11 {
717 name,
718 internal_name,
719 typ,
720 data: ResourceRef { index },
721 })
722 }
723
724 fn write_to<T: Write + Seek>(
725 &self,
726 data: &mut T,
727 encoding: Encoding,
728 img: &AbmpImage,
729 ) -> Result<()> {
730 data.write_fstring("absnddat11", 0x10, encoding, 0, false)?;
731 let name = encode_string(encoding, &self.name, true)?;
732 data.write_u16(name.len() as u16)?;
733 data.write_all(&name)?;
734 let internal_name = encode_string(encoding, &self.internal_name, true)?;
735 data.write_u16(internal_name.len() as u16)?;
736 data.write_all(&internal_name)?;
737 data.write_u8(self.typ)?;
738 let res = img
739 .resources
740 .get(self.data.index)
741 .ok_or_else(|| anyhow::anyhow!("Resource index {} out of bounds", self.data.index))?;
742 data.write_u32(res.len() as u32)?;
743 data.write_all(res)?;
744 Ok(())
745 }
746}
747
748#[derive(Clone, Debug, Serialize, Deserialize)]
749#[serde(tag = "@type")]
750enum AbmpResource {
751 Data(AbData),
752 Image10(AbImage10),
753 ImgData15(AbImgData15),
754 ImgData14(AbImgData14),
755 ImgData13(AbImgData13),
756 ImgData11(AbImgData11),
757 Sound10(AbSound10),
758 SndData12(AbSndData12),
759 SndData11(AbSndData11),
760}
761
762impl AbmpRes for AbmpResource {
763 fn read_from<T: Read + Seek>(
764 data: &mut T,
765 encoding: Encoding,
766 img: &mut AbmpImage,
767 ) -> Result<Self> {
768 let tag = data.peek_fstring(0x10, encoding, true)?;
769 if tag.starts_with("abdata") {
770 return Ok(AbmpResource::Data(AbData::read_from(data, encoding, img)?));
771 }
772 match tag.as_str() {
773 "abimage10" => Ok(AbmpResource::Image10(AbImage10::read_from(
774 data, encoding, img,
775 )?)),
776 "abimgdat15" => Ok(AbmpResource::ImgData15(AbImgData15::read_from(
777 data, encoding, img,
778 )?)),
779 "abimgdat14" => Ok(AbmpResource::ImgData14(AbImgData14::read_from(
780 data, encoding, img,
781 )?)),
782 "abimgdat13" => Ok(AbmpResource::ImgData13(AbImgData13::read_from(
783 data, encoding, img,
784 )?)),
785 "abimgdat11" => Ok(AbmpResource::ImgData11(AbImgData11::read_from(
786 data, encoding, img,
787 )?)),
788 "absound10" => Ok(AbmpResource::Sound10(AbSound10::read_from(
789 data, encoding, img,
790 )?)),
791 "absnddat11" => Ok(AbmpResource::SndData11(AbSndData11::read_from(
792 data, encoding, img,
793 )?)),
794 "absnddat12" => Ok(AbmpResource::SndData12(AbSndData12::read_from(
795 data, encoding, img,
796 )?)),
797 _ => {
798 anyhow::bail!("Unknown Abmp resource tag: {}", tag);
799 }
800 }
801 }
802
803 fn write_to<T: Write + Seek>(
804 &self,
805 data: &mut T,
806 encoding: Encoding,
807 img: &AbmpImage,
808 ) -> Result<()> {
809 match self {
810 AbmpResource::Data(res) => res.write_to(data, encoding, img),
811 AbmpResource::Image10(res) => res.write_to(data, encoding, img),
812 AbmpResource::ImgData15(res) => res.write_to(data, encoding, img),
813 AbmpResource::ImgData14(res) => res.write_to(data, encoding, img),
814 AbmpResource::ImgData13(res) => res.write_to(data, encoding, img),
815 AbmpResource::ImgData11(res) => res.write_to(data, encoding, img),
816 AbmpResource::Sound10(res) => res.write_to(data, encoding, img),
817 AbmpResource::SndData12(res) => res.write_to(data, encoding, img),
818 AbmpResource::SndData11(res) => res.write_to(data, encoding, img),
819 }
820 }
821}
822
823#[derive(Clone, Debug, Serialize, Deserialize)]
825struct AbmpImage {
826 version: u8,
828 datas: Vec<AbmpResource>,
829 extra: Vec<u8>,
830 #[serde(skip)]
831 resources: Vec<Vec<u8>>,
832 #[serde(skip)]
834 resource_filenames: Vec<String>,
835}
836
837fn is_false(b: &bool) -> bool {
838 !*b
839}
840
841#[derive(Clone, Debug, Serialize, Deserialize)]
842struct Resource {
843 path: String,
844 #[serde(skip_serializing_if = "is_false", default)]
845 ambp10: bool,
846}
847
848#[derive(Clone, Debug, Serialize, Deserialize)]
849struct AbmpImage2 {
850 version: u8,
851 datas: Vec<AbmpResource>,
852 #[serde(skip_serializing_if = "Vec::is_empty", default)]
853 extra: Vec<u8>,
854 resources: Vec<Resource>,
855}
856
857impl AbmpImage {
858 pub fn new_from<T: Read + Seek>(reader: &mut T, encoding: Encoding) -> Result<Self> {
859 let magic = reader.read_fstring(16, encoding, true)?;
860 if !magic.starts_with("abmp1") {
861 anyhow::bail!("Not a valid Abmp image");
862 }
863 let version = magic.as_bytes()[5] - b'0' + 10;
864 let mut img = AbmpImage {
865 version,
866 datas: Vec::new(),
867 resources: Vec::new(),
868 resource_filenames: Vec::new(),
869 extra: Vec::new(),
870 };
871 let len = reader.stream_length()?;
872 let mut pos = reader.stream_position()?;
873 while pos < len - 16 {
874 let data = AbmpResource::read_from(reader, encoding, &mut img)?;
875 img.datas.push(data);
876 pos = reader.stream_position()?;
877 }
878 if pos < len {
879 img.extra = reader.read_exact_vec((len - pos) as usize)?;
880 }
881 Ok(img)
882 }
883
884 pub fn dump_to<T: Write + Seek>(&self, mut writer: T, encoding: Encoding) -> Result<()> {
885 writer.write_fstring(
886 &format!("abmp1{}", (self.version - 10 + b'0') as char),
887 16,
888 encoding,
889 0,
890 false,
891 )?;
892 for data in &self.datas {
893 data.write_to(&mut writer, encoding, self)?;
894 }
895 writer.write_all(&self.extra)?;
896 Ok(())
897 }
898
899 fn to_image2(&self) -> AbmpImage2 {
900 AbmpImage2 {
901 version: self.version,
902 datas: self.datas.clone(),
903 resources: Vec::new(),
904 extra: self.extra.clone(),
905 }
906 }
907
908 fn from_image2(img: &AbmpImage2) -> Self {
909 AbmpImage {
910 version: img.version,
911 datas: img.datas.clone(),
912 resources: Vec::new(),
913 resource_filenames: Vec::new(),
914 extra: img.extra.clone(),
915 }
916 }
917}
918
919#[derive(Debug)]
920pub struct Abmp10Image {
921 img: AbmpImage,
922 encoding: Encoding,
923 config: ExtraConfig,
924}
925
926impl Abmp10Image {
927 pub fn new<T: Read + Seek>(
928 mut data: T,
929 encoding: Encoding,
930 config: &ExtraConfig,
931 ) -> Result<Self> {
932 let img = AbmpImage::new_from(&mut data, encoding)?;
933 Ok(Abmp10Image {
934 img,
935 encoding,
936 config: config.clone(),
937 })
938 }
939
940 fn output_resource(
941 &self,
942 folder_path: &std::path::PathBuf,
943 path: String,
944 data: &[u8],
945 encoding: Encoding,
946 ) -> Result<Resource> {
947 let mut res = Resource {
948 path,
949 ambp10: false,
950 };
951 if self.config.qlie_abmp10_process_abmp10
952 && data.len() > 6
953 && data.starts_with(b"abmp1")
954 && data[5] >= b'0'
955 && data[5] <= b'2'
956 {
957 res.ambp10 = true;
958 let another = Abmp10Image::new(MemReaderRef::new(data), self.encoding, &self.config)?;
959 let mut np = std::path::PathBuf::from(&res.path);
960 np.set_extension(another.custom_output_extension());
961 res.path = np.to_string_lossy().to_string();
962 let path = folder_path.join(&res.path);
963 make_sure_dir_exists(&path)?;
964 another.custom_export(&path, encoding)?;
965 } else {
966 let path = folder_path.join(&res.path);
967 make_sure_dir_exists(&path)?;
968 std::fs::write(&path, data)?;
969 }
970 Ok(res)
971 }
972}
973
974impl Script for Abmp10Image {
975 fn default_output_script_type(&self) -> OutputScriptType {
976 OutputScriptType::Custom
977 }
978
979 fn is_output_supported(&self, output: OutputScriptType) -> bool {
980 matches!(output, OutputScriptType::Custom)
981 }
982
983 fn default_format_type(&self) -> FormatOptions {
984 FormatOptions::None
985 }
986
987 fn custom_output_extension<'a>(&'a self) -> &'a str {
988 if self.config.custom_yaml {
989 "yaml"
990 } else {
991 "json"
992 }
993 }
994
995 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
996 let file = std::fs::File::create(filename)?;
997 let mut file = std::io::BufWriter::new(file);
998 let mut img = self.img.to_image2();
999 let mut base_path = filename.to_path_buf();
1000 base_path.set_extension("");
1001 for (res, res_name) in self
1002 .img
1003 .resources
1004 .iter()
1005 .zip(self.img.resource_filenames.iter())
1006 {
1007 let res_name = sanitize_path(res_name);
1008 let res = self.output_resource(&base_path, res_name, res, encoding)?;
1009 img.resources.push(res);
1010 }
1011 let s = if self.config.custom_yaml {
1012 serde_yaml_ng::to_string(&img)?
1013 } else {
1014 serde_json::to_string_pretty(&img)?
1015 };
1016 let s = encode_string(encoding, &s, false)?;
1017 file.write_all(&s)?;
1018 Ok(())
1019 }
1020
1021 fn custom_import<'a>(
1022 &'a self,
1023 custom_filename: &'a str,
1024 file: Box<dyn WriteSeek + 'a>,
1025 encoding: Encoding,
1026 output_encoding: Encoding,
1027 ) -> Result<()> {
1028 create_file(
1029 custom_filename,
1030 file,
1031 encoding,
1032 output_encoding,
1033 &self.config,
1034 )
1035 }
1036}
1037
1038fn create_file<'a>(
1039 filename: &str,
1040 mut writer: Box<dyn WriteSeek + 'a>,
1041 encoding: Encoding,
1042 file_encoding: Encoding,
1043 config: &ExtraConfig,
1044) -> Result<()> {
1045 let data = crate::utils::files::read_file(filename)?;
1046 let s = decode_to_string(file_encoding, &data, true)?;
1047 let img2: AbmpImage2 = if config.custom_yaml {
1048 serde_yaml_ng::from_str(&s)?
1049 } else {
1050 serde_json::from_str(&s)?
1051 };
1052 let mut img = AbmpImage::from_image2(&img2);
1053 let mut base_path = std::path::PathBuf::from(filename);
1054 base_path.set_extension("");
1055 for res in &img2.resources {
1056 let path = base_path.join(&res.path);
1057 let buf = if res.ambp10 {
1058 let mut mem = MemWriter::new();
1059 create_file(
1060 &path.to_string_lossy(),
1061 Box::new(&mut mem),
1062 encoding,
1063 file_encoding,
1064 config,
1065 )?;
1066 mem.into_inner()
1067 } else {
1068 crate::utils::files::read_file(&path)?
1069 };
1070 img.resources.push(buf);
1071 }
1072 img.dump_to(&mut writer, encoding)?;
1073 Ok(())
1074}